package com.thread;

import com.alibaba.fastjson.JSONObject;
import com.framework.config.OcrConfig;
import com.framework.domain.IdCardBack;
import com.framework.domain.OcrDTO;
import com.framework.enums.PaddleHubModuleEnum;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ocr.paddle.LocalHubOcrUtils;
import com.ocr.paddle.domain.LocalHubOcrResultDTO;
import com.ocr.paddle.domain.OCRHubResultDTO;
import com.utils.EasyPoiUtil;
import com.utils.IDCardUtil;
import com.utils.NumberUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 文件名称： com.thread.PaddleOcrThread.java</br>
 * 初始作者： AirOrangeWorkSpace</br>
 * 创建日期： 2020/12/25 11:02</br>
 * 功能说明： 飞浆OCR 处理 <br/>
 * =================================================<br/>
 * 修改记录：<br/>
 * 修改作者        日期       修改内容<br/>
 * ================================================<br/>
 * Copyright (c) 2020-2021 .All rights reserved.<br/>
 */
@Data
@Slf4j
public class PaddleOcrThread implements Runnable {
    private OcrConfig ocrConfig;
    private int face = 1;

    public PaddleOcrThread(OcrConfig ocrConfig) {
        this.ocrConfig = ocrConfig;
    }

    @Override
    public void run() {
        long threadStartTime = System.currentTimeMillis();
        log.info("-------------------识别服务 已开始---------------------------------");
        String ocrFolderPath = ocrConfig.getOcrFolderPath();
        String ocrFile = ocrConfig.getOcrFile();
        List<File> allFiles = Lists.newArrayList();
        File singleFile = new File(ocrFile);
        if (singleFile.exists() && singleFile.isFile()) {
            allFiles.add(singleFile);
        }
        File folderPath = new File(ocrFolderPath);
        if (folderPath.exists() && folderPath.isDirectory()) {
            // 只取图片类文件 png jpg
            func(folderPath, allFiles);
        }
        int imageCount = allFiles.size();
        log.info("共加载图片" + imageCount + "张");

        OcrConfig.PaddleOcr paddleOcr = ocrConfig.getPaddleOcr();
        String url = paddleOcr.getUrl();
        int port = paddleOcr.getPort();
        Map<String, String> moduleMap = paddleOcr.getModuleMap();
        boolean analysis = paddleOcr.isAnalysis();
        String textOcrUrl = "";
        String faceOcrUrl = "";
        for (String s : moduleMap.keySet()) {
            if (PaddleHubModuleEnum.CHINESE_OCR_DB_CRNN_MOBILE.getName().equals(s)) {
                textOcrUrl = "http://" + url + ":" + port + "/predict/" + s;
            }
            if (PaddleHubModuleEnum.PYRAMIDBOX_LITE_SERVER.getName().equals(s)) {
                faceOcrUrl = "http://" + url + ":" + port + "/predict/" + s;
            }
        }

        log.info("开始OCR 工作------------------------------");
        List<LocalHubOcrResultDTO> localOcrResultDTOList = LocalHubOcrUtils.localAllOcr(textOcrUrl, faceOcrUrl, allFiles);
        if (localOcrResultDTOList.size() == 0) {
            log.info("程序 总耗时" + (System.currentTimeMillis() - threadStartTime) / 1000 + "S");
            log.info("OCR 工作结束------------------------------");
            return;
        }
        List<Map<String, Object>> ocrResult = new ArrayList<>();
        Map<String, Object> ocrMap;
        for (LocalHubOcrResultDTO localHubOcrResultDTO : localOcrResultDTOList) {
            ocrMap = Maps.newHashMap();
            ocrMap.put("图片地址", localHubOcrResultDTO.getFilePath());
            ocrMap.put("父文件夹地址", localHubOcrResultDTO.getParentPath());
            ocrMap.put("结果字符串", JSONObject.toJSONString(localHubOcrResultDTO.getOcrHubResultDTOS()));
            ocrMap.put("人脸数", localHubOcrResultDTO.getFaceCount().toString());
            ocrResult.add(ocrMap);
        }
        if (ocrResult.size() > 0) {
            try {
                log.info("导出OCR 识别结果");
                EasyPoiUtil.exportBigExcel("OCR识别结果", ocrResult);
            } catch (Exception e) {
                log.error(e.getMessage());
                log.info("OCR识别结果导出失败");
            }
        }
        if (!analysis) {
            return;
        }

        log.info("开始处理OCR数据------------------------------");
        List<OcrDTO> successOcr = Lists.newArrayList();
        List<OcrDTO> errorOcr = Lists.newArrayList();
        List<String> idCardBackList = Lists.newArrayList(IdCardBack.TEXT);
        for (LocalHubOcrResultDTO localOcrResultDTO : localOcrResultDTOList) {
            String filePath = localOcrResultDTO.getFilePath();
            String parentPath = localOcrResultDTO.getParentPath();
            int faceCount = localOcrResultDTO.getFaceCount();
            List<OCRHubResultDTO> ocrHubResultDTOS = localOcrResultDTO.getOcrHubResultDTOS();

            OcrDTO ocrDTO = new OcrDTO();
            ocrDTO.setFaceCount(faceCount);
            ocrDTO.setFilePath(filePath);
            ocrDTO.setParentFilePath(parentPath);
            ocrDTO.setOcrJson(JSONObject.toJSONString(ocrHubResultDTOS));
            boolean isCard = false;
            boolean isCardBack = false;
            for (OCRHubResultDTO ocrResultItemDTO : ocrHubResultDTOS) {
                String text = ocrResultItemDTO.getText();
                // 若是身份证反面则设置并跳出循环
                if (idCardBackList.contains(text)) {
                    isCardBack = true;
                    ocrDTO.setReference("身份证反面");
                    log.info("成功识别一个 身份证反面");
                    successOcr.add(ocrDTO);
                    break;
                }
                String number = NumberUtils.getNumber(text);
                // 若最后一位是X 则自动替补
                if (number.length() == 17) {
                    number = number + "X";
                }
                if (number.length() == 18) {
                    // 检测身份证合法性
                    String s = IDCardUtil.IDCardValidate(number);
                    log.info(s);
                    isCard = s.equals("YES");
                    // 若有一张脸则认为是身份证正面反之则为手持
                    if (faceCount > 1) {
                        log.info("成功识别一个 身份证手持");
                        ocrDTO.setReference("手持");
                    } else {
                        ocrDTO.setReference("身份证正面");
                    }
                    // 是身份证
                    if (isCard) {
                        ocrDTO.setRemark(null);
                        ocrDTO.setIdCard(number);
                        log.info("成功识别一个 身份证正面");
                        successOcr.add(ocrDTO);
                        break;
                    } else {
                        ocrDTO.setRemark(s);
                    }
                } else {
                    // 若检测不出18位数也不是身份证反面则认为识别失效
                    ocrDTO.setReference("未识别出");
                }
            }
            // 不是正面也不是反面也未读出身份证号
            if (!isCard && !isCardBack) {
                // 可能为手持未读出身份证号
                if (faceCount > 1) {
                    ocrDTO.setReference("可能为手持未读出身份证号");
                }
                // 可能为正面未读出身份证号
                if (faceCount == 1) {
                    ocrDTO.setReference("可能为正面未读出身份证号");
                }
                errorOcr.add(ocrDTO);
            }
        }

        log.info("开始导出结果EXCEL------------------------------");
        List<Map<String, Object>> allResult = new ArrayList<>();
        List<Map<String, Object>> successResult = new ArrayList<>();
        Map<String, Object> valueMap;
        for (OcrDTO ocrDTO : successOcr) {
            valueMap = Maps.newHashMap();
            valueMap.put("图片地址", ocrDTO.getFilePath());
            valueMap.put("图片上级目录地址", ocrDTO.getParentFilePath());
            valueMap.put("ocrJson串", ocrDTO.getOcrJson());
            valueMap.put("身份证号", ocrDTO.getIdCard());
            valueMap.put("有效期", ocrDTO.getTime());
            valueMap.put("备注", ocrDTO.getRemark());
            valueMap.put("人脸数", ocrDTO.getFaceCount());
            valueMap.put("参考值", ocrDTO.getReference());
            successResult.add(valueMap);
            allResult.add(valueMap);
        }
        List<Map<String, Object>> errorResult = new ArrayList<>();
        for (OcrDTO ocrDTO : errorOcr) {
            valueMap = Maps.newHashMap();
            valueMap.put("图片地址", ocrDTO.getFilePath());
            valueMap.put("图片上级目录地址", ocrDTO.getParentFilePath());
            valueMap.put("ocrJson串", ocrDTO.getOcrJson());
            valueMap.put("身份证号", ocrDTO.getIdCard());
            valueMap.put("有效期", ocrDTO.getTime());
            valueMap.put("备注", ocrDTO.getRemark());
            valueMap.put("人脸数", ocrDTO.getFaceCount());
            valueMap.put("参考值", ocrDTO.getReference());
            errorResult.add(valueMap);
            allResult.add(valueMap);
        }
        if (successResult.size() > 0) {
            try {
                EasyPoiUtil.exportBigExcel("OCR成功结果", successResult);
            } catch (Exception e) {
                log.error(e.getMessage());
                log.info("OCR成功结果导出失败");
            }
        }
        if (errorResult.size() > 0) {
            try {
                EasyPoiUtil.exportBigExcel("OCR失败结果", errorResult);
            } catch (Exception e) {
                log.error(e.getMessage());
                log.info("OCR失败结果导出失败");
            }
        }
        if (allResult.size() > 0) {
            try {
                EasyPoiUtil.exportBigExcel("OCR完整结果", allResult);
            } catch (Exception e) {
                log.error(e.getMessage());
                log.info("OCR完整结果导出失败");
            }
        }
        log.info("程序 总耗时" + (System.currentTimeMillis() - threadStartTime) / 1000 + "S");
        log.info("OCR 工作结束------------------------------");
    }

    private static void func(File file, List<File> fileList) {
        File[] fs = file.listFiles();
        for (File f : fs) {
            if (f.isDirectory()) {
                //若是目录，则递归打印该目录下的文件
                func(f, fileList);
            }
            if (f.isFile()) {
                String imageName = f.getName();
                boolean isJpg = imageName.endsWith(".jpg");
                boolean isJPG = imageName.endsWith(".JPG");
                boolean isPng = imageName.endsWith(".png");
                if (isJPG || isPng || isJpg) {
                    //若是图片加入列表
                    fileList.add(f);
                }
            }
        }
    }
}
